﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using Plugins.Common;
using Plugins.Core;
using StockAccessors;
using StockModel;

namespace Plugins.Base.Common
{
	public abstract class DecisionEngineBase : IDisposable
	{
		#region Attributes

		private IStockDataAccessorInformation _accessorInformation;
		private readonly ObservableCollection<Quotation> _quotationSource;

		#endregion

		#region Properties

		public object SyncRoot { get; private set; }
		public DecisionEngineDataBase Data { get; protected set; }
		public OperationManager OperationManager { get; private set; }

		public IStockDataAccessorInformation AccessorInformation
		{
			get
			{
				return _accessorInformation;
			}
			set
			{
				if (_accessorInformation != value)
				{
					if (_accessorInformation != null)
					{
						_accessorInformation.PropertyChanged -= this.AccessorInformation_PropertyChanged;
					}

					_accessorInformation = value;
					_accessorInformation.PropertyChanged += this.AccessorInformation_PropertyChanged; // Disposed

					this.ManageConnection();
				}
			}
		}

		#endregion

		#region Events

		public event EventHandler<RealtimeNotificationEventArgs> RealtimeNotification;

		#endregion

		#region Public Methods

		protected DecisionEngineBase(DecisionEngineDataBase data, ObservableCollection<Quotation> quotationSource, OperationManager orderExecutor)
		{
			this.Data = data;
			this.OperationManager = orderExecutor;

			_quotationSource = quotationSource;
			
			this.SyncRoot = new object();			
		}
		
		/// <summary>
		/// This method has to be called after any technical generator present on the decision engine
		/// </summary>
		protected void RegisterForNotifications()
		{
			_quotationSource.CollectionChanged += QuotationSource_CollectionChanged; // Disposed
		}

		protected void EvaluateStateMachine()
		{
			lock (this.SyncRoot)
			{
				this.EvaluateStateMachineInternal();
			}
		}

		protected abstract void EvaluateStateMachineInternal();

		protected virtual void OnRealtimeNotification(string message)
		{
			if (this.RealtimeNotification != null)
			{
				this.RealtimeNotification(this, new RealtimeNotificationEventArgs(message));
			}
		}

		#endregion

		#region Event Handlers

		private void AccessorInformation_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (e.PropertyName == "Connected")
			{
				this.ManageConnection();
			}
		}

		private void QuotationSource_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			if (e.Action == NotifyCollectionChangedAction.Add)
			{
				this.Data.PreviousQuotation = this.Data.LastQuotation;
				this.Data.LastQuotation = (Quotation)e.NewItems[e.NewItems.Count - 1]; // Only take the last quotation received (we will receive always one as loaders do it one by one)
                this.OperationManager.CurrentQuotation = this.Data.LastQuotation; 
				this.EvaluateStateMachine();
			}
		}

		#endregion

		#region Private Methods

		private void ManageConnection()
		{
			lock (this.SyncRoot)
			{
				if (_accessorInformation.Connected)
				{
					if (this.Data.PreviousState != DecisionEngineStateBase.None)
					{
						this.Data.State = this.Data.PreviousState;
						this.OnRealtimeNotification("Reconnection detected. Engine on restarted. Current state: " + this.Data.PreviousState);
					}
					this.Data.PreviousState = DecisionEngineStateBase.None;
				}
				else if (this.Data.State != DecisionEngineStateBase.AccessorDisconnected)
				{
					this.Data.PreviousState = this.Data.State;
					this.Data.State = DecisionEngineStateBase.AccessorDisconnected;
					this.OnRealtimeNotification("Disconnection detected. Engine on hold. Current state: " + this.Data.PreviousState);
				}
			}
		}

		#endregion

		#region IDisposable

		protected bool Disposed { get; private set; }

		private void Dispose(bool disposing)
		{
			if (!this.Disposed)
			{
				this.InternalDispose(disposing);
			}

			this.Disposed = true;
		}

		protected virtual void InternalDispose(bool disposing)
		{
			if (disposing)
			{
				if (_accessorInformation != null)
				{
					_accessorInformation.PropertyChanged -= this.AccessorInformation_PropertyChanged;
					_quotationSource.CollectionChanged -= this.QuotationSource_CollectionChanged;
				}
			}

			// Dispose here only unmanaged objects 
			// Don’t use managed objects here because maybe 
			// they have been finalized already
		}

		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}

		~DecisionEngineBase()
		{
			this.Dispose(false);
		}

		#endregion
	}
}
